/*
 * Copyright 2022-2027 中国信息通信研究院云计算与大数据研究所
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package com.service.impl;

import java.math.BigDecimal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;

import com.common.constant.AccountConstant;
import com.common.constant.ChkuplogConstant;
import com.mapper.ChkuplogMapper;
import com.mapper.Transfer;
import com.pojo.Account;
import com.pojo.Chkuplog;
import com.service.IsolationSubService;

@Service
@Scope("prototype")
public class IsolationSubServiceImpl extends Thread implements IsolationSubService{
    @Autowired
    private ChkuplogMapper chkuplogMapper;
    @Autowired
    DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    private IsolationSubServiceImpl isolation;
    
    @Autowired
    private Transfer transfer;
    private final Logger logger = LoggerFactory.getLogger(IsolationSubServiceImpl.class); 
    /**
     * 原子性检测类型：1代表转账线程,2代表检测线程
     */
    private String type;
    
    private Account from;
    
    private Account to;
    
    private BigDecimal money;
    /**
     * 数据库隔离级别RR或者RC
     */
    private String isolationLevel;
    /**
     * 线程通知标识
     */
    private boolean signal = false;
    private boolean isStop = false;
    @Override
	public void run() {
    	if("1".equals(type)) {//转账线程
    		while(true) {
    			if(signal) {
    				isolationTransfer();
            		break;
    			}
    			if(isStop) {
    				break;
    			}
    			try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					logger.error("IsolationSubServiceImpl run isolationTransfer InterruptedException accountFrom {} accountTo {} e {}" 
							,from.getAccount_id(),to.getAccount_id(),e);
				}
    		}
    	}else if("2".equals(type)) {//检测线程
    		String result = isolationCheck();
    		//记录检测结果
    		if(isolationLevel.equals(result)||"".equals(result)) {
            	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_I,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_SUCCESS,"AtomicityServiceImpl isolationCheck success "));
    		}else {
            	insertChkuplog(new Chkuplog(ChkuplogConstant.CHKUPLOG_TYPE_I,System.currentTimeMillis()+"",AccountConstant.ACCOUNT_TRAN_FAILURE,"AtomicityServiceImpl isolationCheck failure check result "+result));
    		}
    			
    		
    	}
	}

	private void isolationTransfer() {
		TransactionStatus transactionStatus = null;
		String accountFrom = from.getAccount_id();
		String accountTo = to.getAccount_id();
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readTransactionalAccount(accountFrom);
    		if(from.getAccount_bale().subtract(money).doubleValue()<0) {
    			logger.info("IsolationSubServiceImpl isolationTransfer Account from  {} money {} is not enough {} ",accountFrom, from.getAccount_bale(),money );
    		}else {
        		Account to = transfer.readTransactionalAccount(accountTo);
        		//更新借方余额和借方科目余额
        		Account af = new Account();
                af.setAccount_id(accountFrom);
                af.setAccount_bale(money);
                transfer.transferFrom(af);
        		logger.info("IsolationSubServiceImpl isolationTransfer update account from {} monney {} success ", accountFrom,money);
                Account at = new Account();
                at.setAccount_id(to.getAccount_id());
                at.setAccount_bale(money);
                transfer.transferTo(at);
        		logger.info("IsolationSubServiceImpl isolationTransfer update accoun to {} monney {} success ", accountTo,money);
    		}
	    	dataSourceTransactionManager.commit(transactionStatus);
    		isolation.setSignal(true);
		} catch (Exception e) {
			isolation.setStop(true);
			logger.error("IsolationSubServiceImpl isolationTransfer rollback accountFrom {} accountTo {} e {}" 
					,accountFrom,accountTo,e);
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("IsolationSubServiceImpl isolationTransfer rollback error accountFrom {} accountTo {} e {}" 
						,accountFrom,accountTo,e);
			}
		}			
	}

	private String isolationCheck() {
		String result = "";
		TransactionStatus transactionStatus = null;
		String accountFrom = from.getAccount_id();
		String accountTo = to.getAccount_id();
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		Account from = transfer.readAccount(accountFrom);
    		Account to = transfer.readAccount(accountTo);
    		isolation.setSignal(true);
    		while(true) {
    			if(signal) {
    				Account fromAfter = transfer.readAccount(accountFrom);
            		Account toAfter = transfer.readAccount(accountTo);
            		if(fromAfter.getAccount_bale().compareTo(from.getAccount_bale())==0&&
            				toAfter.getAccount_bale().compareTo(to.getAccount_bale())==0	) {
            			//数据库隔离级别为RR
                		logger.info("IsolationSubServiceImpl isolationCheck result RR ");
                		result = "RR";
            		}else {
            			if(from.getAccount_bale().subtract(money).compareTo(fromAfter.getAccount_bale())==0&&
            				to.getAccount_bale().add(money).compareTo(toAfter.getAccount_bale())==0	){
            					//数据库隔离级别为RC
                    		logger.info("IsolationSubServiceImpl isolationCheck result RC ");
                    		result = "RC";
            			}else {
            				//数据库隔离级别既不是RR也不是RC
                    		logger.info("IsolationSubServiceImpl isolationCheck result not RR and RC ");
                    		result = "NO";
            			}
            			
            		}
            		break;
    			}
    			if(isStop) {
    				result="";
    				break;
    			}
    			Thread.sleep(500);
    		}
	    	dataSourceTransactionManager.commit(transactionStatus);
		} catch (Exception e) {
			isolation.setStop(true);
			result = "";
			logger.error("IsolationSubServiceImpl isolationCheck rollback accountFrom {} accountTo {} e {}" 
					,accountFrom,accountTo,e);
			try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error("IsolationSubServiceImpl isolationCheck rollback error accountFrom {} accountTo {} e {}" 
						,accountFrom,accountTo,e);
			}
		}		
    	return result;
	}

	/**
	 * 插入列表
	 * @param chkuplog
	 */
    public void insertChkuplog(Chkuplog chkuplog) {
    	TransactionStatus transactionStatus = null;
    	try {
    		transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    		chkuplogMapper.insertChkuplog(chkuplog);
    		logger.info(" IsolationSubServiceImpl insertChkuplog  success ");
        	dataSourceTransactionManager.commit(transactionStatus);//提交
		} catch (Exception e) {
			logger.error(" IsolationSubServiceImpl insertChkuplog  rollback e {}" ,e);
	    	try {
		    	dataSourceTransactionManager.rollback(transactionStatus);
			} catch (Exception e2) {
				logger.error(" IsolationSubServiceImpl insertChkuplog  rollback e {}" ,e);
			}
		}
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}



	public Account getFrom() {
		return from;
	}



	public void setFrom(Account from) {
		this.from = from;
	}



	public Account getTo() {
		return to;
	}



	public void setTo(Account to) {
		this.to = to;
	}



	public BigDecimal getMoney() {
		return money;
	}



	public void setMoney(BigDecimal money) {
		this.money = money;
	}



	public IsolationSubServiceImpl getIsolation() {
		return isolation;
	}



	public void setIsolation(IsolationSubServiceImpl isolation) {
		this.isolation = isolation;
	}



	public boolean isSignal() {
		return signal;
	}



	public void setSignal(boolean signal) {
		this.signal = signal;
	}

	public String getIsolationLevel() {
		return isolationLevel;
	}

	public void setIsolationLevel(String isolationLevel) {
		this.isolationLevel = isolationLevel;
	}

	public boolean isStop() {
		return isStop;
	}

	public void setStop(boolean isStop) {
		this.isStop = isStop;
	}

	
}